﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Xml.Linq;

namespace Dxr.Data.Mapping
{
    public class MappingInfo : Zongsoft.Data.IDataAccessMapper
    {
        #region 字段
        private List<ClassNode> _classNodeList;
        private List<ProcedureNode> _procedureNodeList;
        #endregion

        #region 属性
        public List<ClassNode> ClassNodeList
        {
            get
            {
                return _classNodeList;
            }
        }

        public List<ProcedureNode> ProcedureNodeList
        {
            get
            {
                return _procedureNodeList;
            }
        }
        #endregion

        #region
        public void Map(Type type, string name = null)
        {
        }

        public void Map<T>(string name = null)
        {
        }

        public string Get(Type type)
        {
            if (type == null)
                throw new ArgumentNullException(nameof(type));

            ClassNodeList.FirstOrDefault(p => p.EntityType.Equals(type));

            //if (_mapping.TryGetValue(type, out result))
            //    return result;

            throw new InvalidOperationException($"Missing data access mapping of the '{type.FullName}' type.");
        }

        public string Get<T>()
        {
            return this.Get(typeof(T));
        }
        #endregion

        #region 静态方法
        public static MappingInfo Create(Zongsoft.Data.IDataAccessMapper mapper)
        {
            var result = new MappingInfo();
            result._classNodeList = new List<ClassNode>();
            result._procedureNodeList = new List<ProcedureNode>();

            var contexts = GetMappingContext(AppDomain.CurrentDomain.BaseDirectory);

            Dictionary<string, string> dicJoin = new Dictionary<string, string>();
            XElement xml;
            foreach (var item in contexts)
            {
                xml = XElement.Parse(item.Value);
                foreach (var element in xml.Elements())
                {
                    if (!element.Name.LocalName.Equals("procedure", StringComparison.OrdinalIgnoreCase))
                    {
                        var info = ClassNode.Create(element);
                        info.MappingFileFullName = item.Key;

                        var temp = result._classNodeList.FirstOrDefault(p => p.Name.Equals(info.Name, StringComparison.OrdinalIgnoreCase));
                        if (temp != null)
                            throw new Exception(string.Format("文件[{0}]和文件[{1}]中同时存在\"{2}\"节点", temp.MappingFileFullName, info.MappingFileFullName, info.Name));

                        result._classNodeList.Add(info);

                        mapper.Map(info.EntityType, info.Name);
                    }
                    else
                    {
                        var info = ProcedureNode.Create(element);
                        info.MappingFileFullName = item.Key;

                        var temp = result._procedureNodeList.FirstOrDefault(p => p.Name.Equals(info.Name, StringComparison.OrdinalIgnoreCase));
                        if (temp != null)
                            throw new Exception(string.Format("文件[{0}]和文件[{1}]中同时存在\"{2}\"节点", temp.MappingFileFullName, info.MappingFileFullName, info.Name));

                        result._procedureNodeList.Add(info);
                    }
                }
            }

            result._classNodeList.ForEach(p =>
            {
                p.Init(result._classNodeList);

                p.JoinList.ForEach(pp =>
                {
                    pp.Init(p, result._classNodeList);
                });
            });

            return result;
        }

        private static IDictionary<string, string> GetMappingContext(string rootDirectory)
        {
            if (string.IsNullOrWhiteSpace(rootDirectory))
                throw new ArgumentNullException(nameof(rootDirectory));

            var paths = Directory.GetFiles(rootDirectory, "*.mapping", SearchOption.AllDirectories);
            var result = new Dictionary<string, string>();

            foreach (var path in paths)
            {
                result.Add(path, File.ReadAllText(path));
            }

            return result;
        }

        [Obsolete]
        private static Dictionary<string, string> GetMappingContext(string[] paths, string mappingFileName)
        {
            var result = new Dictionary<string, string>();

            foreach (var path in paths)
            {
                if (string.IsNullOrWhiteSpace(path) || result.ContainsKey(path))
                    continue;

                var di = new DirectoryInfo(path);

                if (di.Exists)
                {
                    var files = System.IO.Directory.GetFiles(path, mappingFileName + ".mapping", System.IO.SearchOption.AllDirectories);

                    var temp = GetMappingContext(files, mappingFileName);
                    foreach (var item in temp)
                    {
                        result.Add(item.Key, item.Value);
                    }
                }
                else
                {
                    var fi = new FileInfo(path);

                    if (fi.Exists)
                    {
                        using (var sr = fi.OpenText())
                        {
                            var text = sr.ReadToEnd();
                            result.Add(path, text);
                        }
                    }
                }
            }

            return result;
        }

        private static string ParsePath(string path)
        {
            if (!path.Contains("..\\"))
                return path;

            var index = path.IndexOf("..\\");
            var directory = path.Substring(0, index);
            if (string.IsNullOrEmpty(directory))
                directory = Path.GetDirectoryName(typeof(MappingInfo).Assembly.Location);
            directory = Directory.GetParent(directory).FullName;

            path = Path.Combine(directory, path.Substring(index + 3));
            return ParsePath(path);
        }

        public static bool GetAttribuleValue(XElement element, string name, out string value)
        {
            var attribule = element.Attribute(name);
            if (attribule == null)
            {
                value = string.Empty;
                return false;
            }
            value = attribule.Value.Trim();

            if (string.IsNullOrWhiteSpace(value))
                return false;

            return true;
        }
        #endregion
    }
}
